
package com.UnionPayCenter.account.manager.impl;

import com.UnionPayCenter.account.entity.Account;
import com.UnionPayCenter.account.entity.ChargeBill;
import com.UnionPayCenter.account.entity.FundsBill;
import com.UnionPayCenter.account.enums.AccountErrorCodeEnum;
import com.UnionPayCenter.account.enums.AccountTypeEnum;
import com.UnionPayCenter.account.enums.BillDutyEnum;
import com.UnionPayCenter.account.enums.ChargeStatusEnum;
import com.UnionPayCenter.account.enums.FundsBillTypeEnum;
import com.UnionPayCenter.account.manager.ChargeBillManager;
import com.UnionPayCenter.account.service.AccountService;
import com.UnionPayCenter.account.service.FundsBillService;
import com.UnionPayCenter.entity.ShopInfoEntity;
import com.UnionPayCenter.entity.UserEntity;
import com.UnionPayCenter.entity.vo.UserVo;
import com.UnionPayCenter.enums.ErrorCodeEnum;
import com.UnionPayCenter.exception.ApiException;
import com.UnionPayCenter.mapper.ChargeBillMapper;
import com.UnionPayCenter.mapper.ShopInfoMapper;
import com.UnionPayCenter.mapper.UserMapper;
import com.UnionPayCenter.service.IUserService;
import com.UnionPayCenter.service.IWechatService;
import com.UnionPayCenter.service.ServiceTemplate;
import com.UnionPayCenter.util.Asserts;
import com.UnionPayCenter.util.IdGen;
import com.github.pagehelper.Page;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;


@Service
public class ChargeBillManagerImpl implements ChargeBillManager {

    @Autowired
    private ChargeBillMapper chargeBillMapper;

    @Autowired
    private AccountService accountService;

    @Autowired
    private ShopInfoMapper shopInfoMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private IUserService userService;

    @Autowired
    private IWechatService wechatService;

    @Autowired
    private FundsBillService fundsBillService;

    @Autowired
    private ServiceTemplate serviceTemplate;


    @Override
    @Transactional
    public String saveChargeBill(ChargeBill chargeBill) {
        Asserts.notNull(chargeBill, "充值流水");

        Asserts.naturalNumber(chargeBill.getUserId(), "用户id");
        Asserts.notNull(chargeBill.getAccountType(), "账户类型");
        Asserts.naturalNumber(chargeBill.getAccountId(), "账户Id");

        Asserts.naturalNumber(chargeBill.getAmount(), "金额");

        // 收款方账户类型: 0平台内虚户,1平台内实户,2平台外实户
        //TODO 非余额支付是否需要校验
        //Asserts.notBlank(chargeBill.getBankCardNo(), "支付账户");
        //Asserts.notBlank(chargeBill.getBankCardName(), "支付账户名称");
        //Asserts.notBlank(chargeBill.getCustomerName(), "用户名");
        //Asserts.notBlank(chargeBill.getPayeeAccountType(), "收款方账户类型");
        //Asserts.notBlank(chargeBill.getPayeeMerchantId(), "收款方商户Id");
        //Asserts.notBlank(chargeBill.getPayeeBankCardNo(), "收款方账号");

        if (StringUtils.isBlank(chargeBill.getBankCardNo())) {
            chargeBill.setBankCardNo("rewards");
        }
        if (StringUtils.isBlank(chargeBill.getBankCardName())) {
            chargeBill.setBankCardName("rewards");
        }

        if (StringUtils.isBlank(chargeBill.getSn())) {
            chargeBill.setSn(IdGen.newStringId());
        }
        if (StringUtils.isBlank(chargeBill.getThirdSn())) {
            chargeBill.setThirdSn(chargeBill.getSn());
        }
        if (chargeBill.getBillDuty() == null) {
            chargeBill.setBillDuty(BillDutyEnum.CHARGE);
        }
        // 没有设置原支付金额，使用金额
        if (chargeBill.getTotalAmount() == null) {
            chargeBill.setTotalAmount(chargeBill.getAmount());
        }
        chargeBill.setThirdSn(chargeBill.getThirdSn());
        chargeBill.setChargeType(1);
        chargeBill.setBankType("third");
        if (chargeBill.getState() == null) {
            chargeBill.setState(ChargeStatusEnum.CHARGEING);
        }
        chargeBill.setCreateTime(new Date());
        chargeBill.setUpdateTime(new Date());
        chargeBillMapper.insert(chargeBill);

        return chargeBill.getSn();
    }

    @Override
    @Transactional
    public void updateState(String sn, ChargeStatusEnum state) {
        Asserts.notBlank(sn, "流水序列号");
        Asserts.notNull(state, "流水状态");

        List<ChargeBill> chargeBills = getBySn(sn);
        if (CollectionUtils.isEmpty(chargeBills)) {
            AccountErrorCodeEnum.ACCOUNT_CHARGE_BILL_NOT_EXIST.exception();
        }
        // TODO while true
        int updateRows = chargeBillMapper.cas(sn, "state", ChargeStatusEnum.CHARGEING.code(), state.code());

        if (state == ChargeStatusEnum.SUCCESS && updateRows >= 1) {

            BillDutyEnum billDutyEnum = BillDutyEnum.CHARGE_AND_PAY;
            UserEntity user = null;
            String chargeTime = null;
            Integer amount = 0;
            Integer balance = 0;
            Integer shopId = null;
            FundsBill chargeFundsBill = new FundsBill();
            for (ChargeBill chargeBill : chargeBills) {
                chargeBill.setState(state);
                accountService.charge(chargeBill.getAccountId(), chargeBill.getAmount(), chargeBill.getPayeeMerchantId());
                FundsBill fundsBill = new FundsBill();
                BeanUtils.copyProperties(chargeBill, fundsBill);
                fundsBill.setId(null);
                fundsBill.setBillType(FundsBillTypeEnum.CHARGE);
                if (fundsBill.getBillDuty() == null) {
                    fundsBill.setBillDuty(BillDutyEnum.CHARGE);
                }
                fundsBill.setPayAccountId(chargeBill.getAccountId());
                fundsBill.setPayChannel(chargeBill.getChannelId());
                if (BillDutyEnum.CHARGE.equals(chargeBill.getBillDuty())) {
                    BeanUtils.copyProperties(fundsBill, chargeFundsBill);
                } else {
                    //fundsBillService.saveFundsBill(fundsBill);
                }

                Account account = accountService.getAccountByAccountId(chargeBill.getAccountId());
                if (account.getAccountType() != AccountTypeEnum.CHARGE) {
                    continue;
                }

                if (user == null) {
                    user = userService.wechatPayGetUser(chargeBill.getUserId());
                }
                if (chargeTime == null) {
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    chargeTime = simpleDateFormat.format(chargeBill.getCreateTime());
                }
                if (chargeBill.getBillDuty() == BillDutyEnum.CHARGE) {
                    billDutyEnum = BillDutyEnum.CHARGE;
                }
                amount = chargeBill.getAmount();
                if (shopId == null) {
                    shopId = chargeBill.getShopId();
                }
            }

            if (BillDutyEnum.CHARGE.equals(billDutyEnum)) {
                UserVo userVo = userService.getUserInfo(user.getUserId());
                chargeFundsBill.setBalance(userVo.getTotalBalance());
                fundsBillService.saveFundsBill(chargeFundsBill);
                wechatService.sendDepositMsg(user, String.valueOf(amount), chargeTime,
                        String.valueOf(userVo.getTotalBalance()), sn,
                        shopId);
            }
        }
    }

    @Override
    public void updateAmount(Long chargeBillId, Integer amount, Integer totalAmount) {
        ChargeBill dbChargeBill = chargeBillMapper.selectByPrimaryKey(chargeBillId);
        ChargeBill chargeBill = new ChargeBill();
        chargeBill.setId(chargeBillId);
        chargeBill.setAmount(amount);
        chargeBill.setTotalAmount(totalAmount);
        chargeBill.setBalance(dbChargeBill.getBalance() - totalAmount + amount);
        chargeBillMapper.updateByPrimaryKeySelective(chargeBill);
    }

    @Transactional
    @Override
    public void updateThirdSn(String sn, String thirdSn) {
        Asserts.notBlank(sn, "流水号");
        Asserts.notBlank(thirdSn, "第三方流水号");

        ChargeBill chargeBill = new ChargeBill();
        chargeBill.setSn(sn);
        chargeBill.setThirdSn(thirdSn);
        chargeBillMapper.updateThirdSn(sn, thirdSn);
        FundsBill fundsBill = new FundsBill();
        fundsBill.setSn(sn);
        fundsBill.setThirdSn(thirdSn);
        fundsBillService.updateFundsBill(fundsBill);
    }

    @Override
    public List<ChargeBill> getBySn(String sn) {
        Asserts.notBlank(sn, "流水号");
        return chargeBillMapper.selectBySn(sn);
    }

    @Override
    public ChargeBill getBySn(String sn, AccountTypeEnum accountTypeEnum) {
        ChargeBill chargeBill = new ChargeBill();
        chargeBill.setAccountType(accountTypeEnum);
        chargeBill.setSn(sn);
        List<ChargeBill> chargeBills = chargeBillMapper.selectByCondition(chargeBill);
        if (CollectionUtils.isEmpty(chargeBills)) {
            return null;
        }
        return chargeBills.get(0);
    }

    @Override
    public Page<ChargeBill> getChargeBillByCondition(ChargeBill chargeBill, Integer page, Integer pageSize) {
        Asserts.notNull(chargeBill, "查询条件");

        // 从店铺中查找商户Id
        // 流水存了商铺代码
        if (chargeBill.getShopId() != null) {
            ShopInfoEntity shop = shopInfoMapper.selectByShop(chargeBill.getShopId());
            if (shop == null) {
                throw new ApiException(ErrorCodeEnum.TK1010);
            }
            chargeBill.setPayeeMerchantId(shop.getMerchantId());
        }

        return getChargeBill(chargeBill, chargeBill.getState(), page, pageSize);
    }

    @Override
    public Long sumAmount(ChargeBill chargeBill) {
        return chargeBillMapper.sumAmount(chargeBill);
    }

    private Page<ChargeBill> getChargeBill(ChargeBill chargeBill, ChargeStatusEnum state, Integer page, Integer pageSize) {
        if (state != null) {
            chargeBill.setState(state);
        }
        Page<ChargeBill> chargeBillPage = serviceTemplate.pagination(page, pageSize, () -> chargeBillMapper.selectByCondition(chargeBill));
        for (ChargeBill resultBill : chargeBillPage) {
            UserVo userVo = userMapper.selectInfo(resultBill.getUserId());
            if (userVo == null) {
                continue;
            }
            resultBill.setMobile(userVo.getMobile());
        }
        return chargeBillPage;
    }
}
